/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package cz.muni.fi.pb138.webrep.metadata.processors;

import cz.muni.fi.pb138.webrep.util.Pair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.basex.core.BaseXException;
import org.basex.core.Context;
import org.basex.core.cmd.Close;
import org.basex.core.cmd.CreateDB;
import org.basex.core.cmd.Open;
import org.basex.core.cmd.XQuery;
/**
 *
 * @author Odin & Kamil
 */
public class WsdlMetadataProcessor {
    private String dbName;
    
    /**
     * WsdlMetadataProcessor allows access to WSDL metadata database
     * 
     * @throws BaseXException 
     */
    public WsdlMetadataProcessor() throws BaseXException{
        dbName = "wsdl_meta";
        Context context = new Context();
        try{
            new Open(dbName).execute(context); 
        }catch(BaseXException e){
            new CreateDB(dbName, "<wsdl_meta/>").execute(context);
        }finally{
            new Close().execute(context);
        }
        
    }
    /**
     * Extract metadata from file 'pathToFile'. pathToFile must be *.wsdl file
     * 
     * @param pathToFile The file from which data will be obtained
     * @return True if success, throws Exception otherwise
     * @throws BaseXException 
     */
    public boolean process(String pathToFile) throws BaseXException{       
        pathToFile = pathToFile.replace("'", "&#39;");
        Context context = new Context();
        try {
            new Open(dbName).execute(context);
            new XQuery(extractFromWSDL_Query(pathToFile)).execute(context);            
        }
        finally {
            new Close().execute(context);
        }
        return true;
    }
    /**
     * Find artifacts which contains metadata named nameOfMeta
     * 
     * @param nameOfMeta Seeking metadata
     * @return Collection of Strings which represents filepaths of artifacts. May return empty Collection
     * @throws BaseXException 
     */
    public ArrayList<String> find(String nameOfMeta) throws BaseXException{       
        Context context = new Context();
        ArrayList<String> artifacts = new ArrayList<>();
        try {
            new Open(dbName).execute(context);            
            String result = new XQuery(findArtifacts_Query2(nameOfMeta)).execute(context);            
            artifacts.addAll(Arrays.asList(result.split("\n")));
        }
        finally {
            new Close().execute(context);
        }
        return artifacts;
    }
    /**
     * Find all artifacts with all versions which matches given constraints
     * 
     * @param constraints map of restrictions, where key is metadata typeand value is searched value
     * @return map, where keys are names of artifacts and values are sets of numbers of matching versions
     * @throws BaseXException 
     */
    public Map<String,Set<Integer>> getArtifactVersionsByMetadata(List<Pair<String,String>> constraints) throws BaseXException{
        String condition = "";
        for (Pair<String,String> constraint : constraints) {
            condition = condition + "descendant::"+constraint.getLeft()+"/@name='"+constraint.getRight()+"' and ";
        }
        condition = condition.substring(0, condition.length() - 5);
        Context context = new Context();
        List<String> res = null;
        try {
            new Open(dbName).execute(context);
            String result = new XQuery(findArtifacts_Query(condition)).execute(context);   
            if(result.length()!=0) res = Arrays.asList(result.split("#%# "));
            res.set(res.size()-1, res.get(res.size()-1).substring(0, res.get(res.size()-1).length()-4));// Vposlednim line je jeste jeden #%# odstran
        }
        finally {
            new Close().execute(context);
        }
        String path = "";
        Map<String,Set<Integer>> ret = new HashMap();
        for(String line: res){            
            path = line.substring(0, line.lastIndexOf("\\"));
            if(!ret.containsKey(path)){
                ret.put(path,new HashSet());
            }            
            ret.get(path).add(Integer.parseInt(line.substring(line.lastIndexOf("\\")+1,line.length())));
        }
        return ret;
    }
    /**
     * Gets metadata of concrete artifact 
     * 
     * @param artifact filepath to artifact
     * @param version version of artifact
     * @return String which represents xml document with root element of concrete artifact
     * @throws BaseXException 
     */    
    public String getMeta(String artifact, String version)throws BaseXException{
        Context context = new Context();
        String result = "";
        try {
            new Open(dbName).execute(context);
            result = new XQuery(getMeta_Query(artifact,Integer.parseInt(version))).execute(context);            
        }
        finally {
            new Close().execute(context);
        }
        return result;
    }
    /**
     * Gets array of names of concrete artifact and concrete tagName
     * 
     * @param artifact filepath to artifact
     * @param version version of artifact
     * @param tagName tagName
     * @return
     * @throws BaseXException 
     */
    public String[] getMeta(String artifact, String version, String tagName)throws BaseXException{
        Context context = new Context();
        String[] res = null;
        try {
            new Open(dbName).execute(context);
            String result = new XQuery(getMeta_Query(artifact,Integer.parseInt(version),tagName)).execute(context);   
            //System.out.println(new XQuery("/").execute(context));   
            if(!result.equals("")) res = result.split(" ");
        }
        finally {
            new Close().execute(context);
        }
        return res;
    }
    /**
     * delete all versions of artifact according to given metaName
     * 
     * @param metaName name of artifact
     * @throws BaseXException 
     */
    public void deleteMetadata(String metaName)throws BaseXException{
        Context context = new Context();
        String name = metaName.substring(0,metaName.lastIndexOf('.'));
        String extension = metaName.substring(metaName.lastIndexOf('.')+1,metaName.length());
        try {
            new Open(dbName).execute(context);
            new XQuery("delete nodes //artifact[matches(@filename,'.*"+name+"\\."+extension+"$')]").execute(context);            
        }
        finally {
            new Close().execute(context);
        }
    }
    
    private String extractFromWSDL_Query(String pathToFile){
        String path = pathToFile.substring(0, pathToFile.lastIndexOf("\\"));
        String query = 
            "let $document := doc('"+pathToFile+"') "+
            "let $wsdl_meta := /"+dbName+" "+
            "let $version := if (exists($wsdl_meta/artifact[@filename='"+path+"'])) "+
                             "then count($wsdl_meta/artifact[@filename])+1 "+
                             "else 1 "+
            "let $artifact := <artifact filename=\""+path+"\" version=\"{$version}\">{ "+
                "for $operation in $document//*[local-name()='portType']/*[local-name()='operation']/@*[local-name()='name'] "+
                "return <operation>{ $operation }</operation>, "+                    
                "for $message in $document//*[local-name()='message'] "+
                "return <message>{ $message/@*[local-name()='name'], "+
                    "for $part in $message/*[local-name()='part'] "+
                    "return <part>{$part/@*[local-name()='name']}</part> "+
                "}</message> "+
            "}</artifact> "+
                
            "return insert node $artifact into $wsdl_meta ";
        return query;
    } 

    private String findArtifacts_Query2(String nameOfMeta){
        String query = 
            "for $meta in /"+dbName+"/artifact[descendant::*/@name='"+nameOfMeta+"'] "+
            "return concat(data($meta/@filename),'\\',data($meta/@version),'\n')";
        return query;
    }

    private String getMeta_Query(String meta,int version){
        return "//artifact[@filename='"+meta+"' and @version='"+version+"'] ";
    }
    
    private String getMeta_Query(String artifact,int version, String meta){
        return  "for $x in //artifact[@filename='"+artifact+"' and @version='"+version+"']//"+meta+"/@name "+
                "order by $x "+
                "return data($x)";
    }
    
    private String findArtifacts_Query(String condition){
        String query = 
            "for $meta in /"+dbName+"/artifact["+condition+"] "+
            "order by data($meta/@filename)"+
            "return concat(data($meta/@filename),'\\',data($meta/@version),'#%#') ";
        return query;        
    }
    /*
    class Pair{
        private final String left;
        private final String right;
        public Pair(String left,String right){
            this.left = left;
            this.right = right;
        }
        public String getLeft(){
            return left;
        }
        public String getRight(){
            return right;
        }
    }
    */
}